package hasthi.common;

import static hasthi.common.HasthiOptions.ADMIN_TOPIC;
import static hasthi.common.HasthiOptions.BOOTSTRAP_NODE;
import static hasthi.common.HasthiOptions.BOOTSTRAP_P2P_PORT;
import static hasthi.common.HasthiOptions.BOOTSTRAP_SOAP_PORT;
import static hasthi.common.HasthiOptions.COORDINATOR_EPOCH_SIZE;
import static hasthi.common.HasthiOptions.*;
import static hasthi.common.HasthiOptions.ELECTION_BROADCAST_TIMEOUT;
import static hasthi.common.HasthiOptions.ELECTION_TIMEOUT;
import static hasthi.common.HasthiOptions.GLOBAL_RULESET;
import static hasthi.common.HasthiOptions.LOCAL_RULESET;
import static hasthi.common.HasthiOptions.MANAGEMENT_AGENT_EPOCH_SIZE;
import static hasthi.common.HasthiOptions.MANAGEMENT_TOPIC;
import static hasthi.common.HasthiOptions.MANAGER_EPOCH_SIZE;
import static hasthi.common.HasthiOptions.MANAGER_TIMEOUT_LIMIT;
import static hasthi.common.HasthiOptions.MSG_BROKER_URL;
import static hasthi.common.HasthiOptions.RESOURCE_TIMEOUT_LIMIT;
import static hasthi.common.HasthiOptions.SSL_HOSTCERTS_KEY_FILE;
import static hasthi.common.HasthiOptions.SSL_TRUSTED_CERTS_FILE;
import static hasthi.common.HasthiOptions.SUCESSOR_COUNT;
import static hasthi.common.HasthiOptions.HASTHI_PROP_FILE;

import hasthi.actuators.ActionCenter;
import hasthi.common.HasthiException.FaultCode;
import hasthi.instrument.FailureDetector;
import hasthi.instrument.PingingFailureDetector;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import org.ietf.jgss.GSSCredential;

import xsul.invoker.http.HttpDynamicInfosetInvoker;

/**
 * This class hold the basic configurations for Hasthi, it is load from
 * hasthi.properties in the current directory or classpath.
 * 
 * @author Srinath Perera(hperera@cs.indiana.edu)
 */

public class BaseContext {
    static {
        System.setProperty(
                HttpDynamicInfosetInvoker.HTTP_TRANSPORT_CLIENT_DISABLE_KEEPALIVE_PROPERTY, "");
    }

    private String trustedCertsFile;
    private String hostKeyfile;
    private GSSCredential credential;

    private String boostrapHost = "linbox3.extreme.indiana.edu";
    private int boostrapP2PPort = 9001;
    private int boostrapSoapPort = 9002;
    private String messageBrokerUrl = "http://tyr14.cs.indiana.edu:12346/";
    private String managementTopic = "hasthi";
    private String adminTopic = "Admin";
    private DistributedLogger dlogger;
    private int sucessorCount = 3;
    private int resourceTimeoutLimitInMinutes = 10;
    private int managerTimeoutLimitInMinutes = 10;
    private String name;
//    private int managerEpochSeconds = 1 * 30;
//    private int coordinatorEpochSeconds = 1 * 30;
//    private int mngAgentEpochSeconds = 1 * 30;
    private int managerEpochSeconds = 5;
    private int coordinatorEpochSeconds = 5;
    private int mngAgentEpochSeconds = 5;
    private long electionTimeoutMs = 15 * 60 * 1000;
    private long electionResponseTimeoutMs = 1 * 60 * 1000;
    private String globalRuleSetFile;
    private String localRuleSetFile;
    private boolean joinViaBootstrap = true;

    private Properties properties;

    private String[][] deploymentHosts;
    private FailureDetector failureDetector;

    public BaseContext() {
        this(null,false);
    }
    
    public BaseContext(Properties config) {
        this(config,false);
    }

    public BaseContext(Properties config,boolean forceLoadfromEnv) {
        try {
            this.properties = new Properties();
            String envSettings = System.getProperty("user.home") + "/.hasthi";
            File envSettingsFile = new File(envSettings);
            if (envSettingsFile.exists()) {
                InputStream envSettingsFileIn = new FileInputStream(envSettingsFile);
                properties.load(envSettingsFileIn);
                envSettingsFileIn.close();
            }

            if (config == null || forceLoadfromEnv) {
                String filename = "hasthi.properties";
                InputStream propertyStream;
                String configFile = System.getProperty(HASTHI_PROP_FILE);
                if (configFile != null) {
                    propertyStream = new FileInputStream(configFile);
                } else {
                    File file = new File(filename);
                    if (file.exists()) {
                        propertyStream = new FileInputStream(file);
                    } else {
                        ClassLoader cl = Thread.currentThread().getContextClassLoader();
                        propertyStream = cl.getResourceAsStream(filename);
                    }
                    if (propertyStream == null) {
                        throw new HasthiError("Can not find property file " + filename);
                    }
                }

                this.properties.load(propertyStream);
                propertyStream.close();
            }
            if(config != null){
                this.properties.putAll(config);
            }
            boostrapHost = Utils.getStringProperty(properties, BOOTSTRAP_NODE, boostrapHost);
            boostrapP2PPort = Utils.getIntProperty(properties, BOOTSTRAP_P2P_PORT, boostrapP2PPort);
            boostrapSoapPort = Utils.getIntProperty(properties, BOOTSTRAP_SOAP_PORT,
                    boostrapSoapPort);
            messageBrokerUrl = Utils
                    .getStringProperty(properties, MSG_BROKER_URL, messageBrokerUrl);
            managementTopic = Utils
                    .getStringProperty(properties, MANAGEMENT_TOPIC, managementTopic);
            adminTopic = Utils.getStringProperty(properties, ADMIN_TOPIC, adminTopic);

            sucessorCount = Utils.getIntProperty(properties, SUCESSOR_COUNT, sucessorCount);
            resourceTimeoutLimitInMinutes = Utils.getIntProperty(properties,
                    RESOURCE_TIMEOUT_LIMIT, resourceTimeoutLimitInMinutes);
            managerTimeoutLimitInMinutes = Utils.getIntProperty(properties, MANAGER_TIMEOUT_LIMIT,
                    managerTimeoutLimitInMinutes);

            managerEpochSeconds = Utils.getIntProperty(properties, MANAGER_EPOCH_SIZE,
                    managerEpochSeconds);
            coordinatorEpochSeconds = Utils.getIntProperty(properties, COORDINATOR_EPOCH_SIZE,
                    coordinatorEpochSeconds);
            mngAgentEpochSeconds = Utils.getIntProperty(properties, MANAGEMENT_AGENT_EPOCH_SIZE,
                    mngAgentEpochSeconds);
            electionTimeoutMs = Utils.getLongProperty(properties, ELECTION_TIMEOUT,
                    electionTimeoutMs);
            electionResponseTimeoutMs = Utils.getLongProperty(properties,
                    ELECTION_BROADCAST_TIMEOUT, electionResponseTimeoutMs);

            globalRuleSetFile = properties.getProperty(GLOBAL_RULESET);
            localRuleSetFile = properties.getProperty(LOCAL_RULESET);

            this.trustedCertsFile = properties.getProperty(SSL_TRUSTED_CERTS_FILE);
            this.hostKeyfile = properties.getProperty(SSL_HOSTCERTS_KEY_FILE);

            // String deploymentHostsAsStr =
            // properties.getProperty(DEPLOYMENT_HOSTS);
            // String[] encodedHosts = deploymentHostsAsStr.split("\\|");
            // if(encodedHosts != null && encodedHosts.length > 0){
            // this.deploymentHosts = new String[encodedHosts.length][];
            // int i = 0;
            // for(String encodedHost:encodedHosts){
            // deploymentHosts[i] = encodedHost.split(",");
            // i++;
            // }
            // }
            dlogger = new DistributedLogger(this);
        } catch (FileNotFoundException e) {
            throw new HasthiError(e);
        } catch (IOException e) {
            throw new HasthiError(e);
        }
    }

    public String getTrustedCertsFile() {
        return trustedCertsFile;
    }

    public String getHostKeyfile() {
        return hostKeyfile;
    }

    public GSSCredential getCredential() {
        return credential;
    }

    public String getBoostrapHost() {
        return boostrapHost;
    }

    public int getBoostrapP2PPort() {
        return boostrapP2PPort;
    }

    public int getBoostrapSoapPort() {
        return boostrapSoapPort;
    }

    public String getMessageBrokerUrl() {
        return messageBrokerUrl;
    }

    public String getManagementTopic() {
        return managementTopic;
    }

    public String getAdminTopic() {
        return adminTopic;
    }

    public DistributedLogger getDlogger() {
        return dlogger;
    }

    public int getSucessorCount() {
        return sucessorCount;
    }

    public int getResourceTimeoutLimitInMinutes() {
        return resourceTimeoutLimitInMinutes;
    }

    // public int getManagerTimeoutLimitInMinutes() {
    // return managerTimeoutLimitInMinutes;
    // }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public static String getSSL_HOSTCERTS_KEY_FILE() {
        return SSL_HOSTCERTS_KEY_FILE;
    }

    public int getManagerEpochSeconds() {
        return managerEpochSeconds;
    }

    public int getCoordinatorEpochSeconds() {
        return coordinatorEpochSeconds;
    }

    public static String getSSL_TRUSTED_CERTS_FILE() {
        return SSL_TRUSTED_CERTS_FILE;
    }

    public long getElectionTimeoutMs() {
        return electionTimeoutMs;
    }

    public long getElectionResponseTimeoutMs() {
        return electionResponseTimeoutMs;
    }

    public String getRuleset() {
        return localRuleSetFile;
    }

    public String getGlobalRuleSet() {
        return globalRuleSetFile;
    }

    public int getMngAgentEpochSeconds() {
        return mngAgentEpochSeconds;
    }

    public String getProperty(String name) {
        return properties.getProperty(name);
    }

    public String[][] getDeploymentHosts() {
        return deploymentHosts;
    }

    public ActionCenter createActuatorBase() throws HasthiException {
        ActionCenter actuatorBase;
        try {
            String acturatorBaseClass = properties.getProperty(ACTUATOR_BASE);
            if (acturatorBaseClass != null) {
                actuatorBase = (ActionCenter) Utils.loadClass(acturatorBaseClass).newInstance();
            } else {
                actuatorBase = new hasthi.actuators.impl.LoggingActuatorBase();
            }
            return actuatorBase;
        } catch (InstantiationException e) {
            throw new HasthiException(e, FaultCode.ConfigError);
        } catch (IllegalAccessException e) {
            throw new HasthiException(e, FaultCode.ConfigError);
        } catch (ClassNotFoundException e) {
            throw new HasthiException(e, FaultCode.ConfigError);
        }
    }
    
    
    public FailureDetector getFailureDetector() throws HasthiException{
        if(failureDetector != null){
            return failureDetector;
        }else{
            try {
                String acturatorBaseClass = properties.getProperty(FAILURE_DETECTOR);
                if (acturatorBaseClass != null) {
                    failureDetector = (FailureDetector) Utils.loadClass(acturatorBaseClass).newInstance();
                } else {
                    failureDetector = new PingingFailureDetector();
                }
                return failureDetector;
            } catch (InstantiationException e) {
                throw new HasthiException(e, FaultCode.ConfigError);
            } catch (IllegalAccessException e) {
                throw new HasthiException(e, FaultCode.ConfigError);
            } catch (ClassNotFoundException e) {
                throw new HasthiException(e, FaultCode.ConfigError);
            }
        }
    }
    

    public boolean isJoinViaBootstrap() {
        return joinViaBootstrap;
    }

    public Properties getProperties() {
        return properties;
    }
}
